<div id="article_content" class="article_content clearfix">
    <div class="article-copyright">
<span class="creativecommons">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">
</a>
<span>
版权声明：本文为博主原创文章，遵循<a href="http://creativecommons.org/licenses/by-sa/4.0/" target="_blank" rel="noopener"> CC 4.0 BY-SA </a>版权协议，转载请附上原文出处链接和本声明。                    </span>
<div class="article-source-link2222">
本文链接：<a href="https://blog.csdn.net/magic_1024/article/details/102676075">https://blog.csdn.net/magic_1024/article/details/102676075</a>
</div>
</span>

</div>
            <!--一个博主专栏付费入口-->
<!--一个博主专栏付费入口结束-->
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-4a3473df85.css">
<div id="content_views" class="markdown_views prism-atom-one-dark">
<!-- flowchart 箭头图标 勿删 -->
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
</svg>
    <blockquote>
<p>在众多的网络协议中，TCP协议占据着举足轻重的地位，你知道什么是TCP协议吗？</p>
</blockquote>
<h3><a name="t0"></a><a name="t0"></a><a id="TCP_1"></a>一、TCP协议</h3>
<p><strong>TCP</strong>(Transmission Control Protoco)<strong>协议</strong>属于计算机网络体系中的<strong>运输层</strong>。运输层的任务是负责向主机中应用层进程之间的通信提供通用的数据传输服务。所以可以通俗理解TCP协议就是进程间数据通讯传输协议。根据不同应用，运输层主要使用TCP和UDP两种协议之一。如果想要了解计算机网络体系分层概念，可以看我的上一篇博文 <a href="https://blog.csdn.net/magic_1024/article/details/102379580" rel="nofollow">计算机网络体系结构划分</a><br>
<img src="https://img-blog.csdnimg.cn/20191026151949972.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21hZ2ljXzEwMjQ=,size_12,color_FFFFFF,t_70" alt="计算机网络体系结构"></p>
<h3><a name="t1"></a><a name="t1"></a><a id="TCP_4"></a>二、TCP协议特点</h3>
<p>TCP协议本身是比较复杂的，它包含拥塞控制、可靠传输、流量控制、连接管理等功能，主要特点包含以下几个方面：</p>
<ul>
<li><strong>TCP是面向连接的协议</strong>，程序在使用TCP协议通讯时，必须要先建立TCP连接。这就好比我要给你打电话，咱俩之间的通讯线路必须是连接状态的。</li>
<li><strong>TCP连接是点对点的</strong>，每一条TCP连接只能有两个<strong>端口</strong>（endpoint）。这个<strong>端点</strong>就是我们Java网络编程所使用的<strong>套接字</strong>（socket）。由IP地址+端口号组成（IP:端口号）。</li>
<li><strong>TCP连接提供可靠交付</strong>，使用TCP连接传输数据，保证无差错、不丢失、不重复并且按顺序到达</li>
<li><strong>TCP连接是双向的通信</strong>，通信的两方，既能发送数据，也能接收数据。就向我们通话时双方一样。</li>
<li><strong>TCP面向字节流传输数据</strong>，TCP传送数据时，是把进程交付的数据，按照一段段字节流序列传递的，每次传输其中一段字节序列。应用层接收字节序列后，再将内容还原。</li>
</ul>
<h3><a name="t2"></a><a name="t2"></a><a id="TCP_12"></a>三、TCP报文格式</h3>
<p>既然TCP协议属于运输层，运输层职责是为主机应用层提供<strong>进程间的数据传输</strong>，所以我们有必要搞清楚TCP协议运输数据时的形式。TCP协议规定了TCP传输数据的单元为<strong>TCP数据报</strong>。TCP数据报是对应用层进程交付数据的封装。<br>
<strong>TCP数据报</strong>由两部分组成：<strong>TCP首部</strong>和<strong>TCP数据部分</strong></p>
<ul>
<li><strong>TCP首部</strong>：包含许多控制和描述字段，是TCP全部功能的体现</li>
<li><strong>TCP数据部分</strong>：对于应用层进程交付的数据，封装后的字节流序列<br>
<img src="https://img-blog.csdnimg.cn/20191026171834356.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21hZ2ljXzEwMjQ=,size_16,color_FFFFFF,t_70" alt="TCP报文格式"><br>
应用层进程交付的数据被封装TCP报文，然后进行传输，TCP连接保证数据传输的可靠性，TCP报文传输的过程示意图：<br>
<img src="https://img-blog.csdnimg.cn/20191026165051431.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21hZ2ljXzEwMjQ=,size_16,color_FFFFFF,t_70" alt="TCP数据传输模型"></li>
</ul>
<h3><a name="t3"></a><a name="t3"></a><a id="TCP_21"></a>四、TCP报文首部</h3>
<p>TCP报文首部定义是TCP协议的精华所在，TCP复杂功能的实现，全部依靠了首部里各种控制字段。我们来看下TCP首部都定义了什么：<br>
<img src="https://img-blog.csdnimg.cn/20191026174747908.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21hZ2ljXzEwMjQ=,size_16,color_FFFFFF,t_70" alt="TCP报文首部"><br>
TCP报文的首部<strong>由20个字节固定字节</strong>和<strong>4n</strong>(n取[0~5]整数)个可变的<strong>选项</strong>字节组成，其中固定部分的各字段含义如下：</p>
<ul>
<li>
<p><strong>源端口</strong>和<strong>目的端口</strong>：各占2个字节，分别写入通信双方进程端口号</p>
</li>
<li>
<p><strong>序号seq</strong>：占4个字节。在TCP连接中，传送的字节流中的每一个字节都是要按顺序编号[0~2<sup>32</sup>-1]，整个要传送的字节流的起始序号在必须连接建立时设置，序号字段值代表<strong>本报文段所发送的数据的第一个字节的序号</strong></p>
</li>
<li>
<p><strong>确认号ack</strong>：占4个字节，是<strong>期望收到对方的下一个报文段的第一个数据字节的序号</strong>，即ack=N,就代表了到序号N-1为止的所有数据都被正确接收了。</p>
</li>
<li>
<p><strong>数据偏移</strong>：占4位，表示TCP报文的数据部分起始处距离TCP报文首部的起始处有多远，数据偏移值的单位是32位字（以4字节为计算单位），4位二进制能表示的值[0~15],这就意味着TCP首部最大长度为60字节，也就是<strong>选项</strong>部分的最大长度为40字节。</p>
</li>
<li>
<p><strong>保留位</strong>：占6位，保留为以后使用，目前置为0</p>
</li>
<li>
<p><strong>控制位</strong>：占6位，每个控制字段占1位，它们的标识和含义是：<br>
-  <strong>紧急URG</strong>:URG=1时，告诉系统此报文中有紧急数据，优先传送，与紧急指针配合使用<br>
- <strong>确认ACK</strong>:<strong>当ACK=1时，确认号才有效</strong>，ACK=0时，确认号无效，TCP连接建立后，所有报文ACK必须都为1<br>
- <strong>推送PSH</strong>：发送方把PSH置为1，接收方收到报文后会尽快交付，不用等缓存填满了再交付<br>
- <strong>复位RST</strong>:当RST=1时，表明TCP连接出现了严重差错，必须释放连接，然后重新建立新运输连接。RST=1还可以用来拒接一个非法报文段或拒绝打开一个连接。<br>
- <strong>同步SYN</strong>:在连接建立时用来同步序号。<strong>当SYN=1而ACK=0时，表明这是一个连接请求报文段，对方若同意建立连接，则需要在响应报文中使SYN=1和ACK=1</strong><br>
-  <strong>终止FIN</strong>:用来释放一个连接 。当<strong>FIN=1时，表明此报文段的发送方数据已经发送完毕，并且要求释放运输连接。</strong></p>
</li>
<li>
<p><strong>窗口</strong>:占2字节，窗口值是[0~2<sup>16</sup>-1]之间的整数。窗口值告诉了对方，从本报文段的确认号算起，允许对方发送的数据量。</p>
</li>
<li>
<p><strong>检验和</strong>：占2字节，用于接收方检验首部和数据部分是否在传输中有差错，类似我们下载文件时的Md5签名校验作用。</p>
</li>
<li>
<p><strong>紧急指针</strong>：占2字节，URG=1时才起作用，用于指明本报文段中的紧急数据的字节数，紧急数据结束后就是普通数据，所以紧急指针指出了紧急数据的末尾在报文中位置。</p>
</li>
<li>
<p><strong>选项</strong>：长度可变，最小0字节，最长达40字节。首部用来动态存储数据。</p>
</li>
</ul>
<h3><a name="t4"></a><a name="t4"></a><a id="_42"></a>五、三次握手过程</h3>
<p>TCP是面向连接的，所以每次传输数据之前，必须要建立TCP连接，在TCP建立连接时主要解决三个层面问题：</p>
<ul>
<li>使连接的每一方都能确认对方的存在</li>
<li>协商连接中参数，比如各方窗口值，时间戳等</li>
<li>各方对运输资源如缓存大小、连接表等进行分配</li>
</ul>
<p>我们都知道TCP连接采用的是C/S模式，主动发起连接的叫<strong>客户端client</strong>,被动等待连接的叫<strong>服务器Server</strong>。那么TCP建立连接的过程是什样的呢？什么是<strong>三次握手</strong>呢？<br>
<img src="https://img-blog.csdnimg.cn/20191026211912237.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21hZ2ljXzEwMjQ=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>
默认情况下客户端client和服务端sever的TCP进程都处于<strong>CLOSED</strong>（关闭）状态。<br>
服务端TCP服务进程先建立传输控制块TCB，然后服务端进入<strong>LISTEN</strong>状态，等待客户端连接请求。</p>
<ul>
<li><strong>第一次握手</strong>：客户端TCP进程也先建立传输控制块TCB，然后向服务端发送连接请求报文段，此时SYN=1,随机选定一个初始序号seq=x,，此报文不能携带数据，但是要消耗掉一个序号，发送完毕后，客户端进入<strong>SYN-SENT</strong>（同步已发送）状态</li>
<li><strong>第二次握手</strong>：服务端收到客户端请求连接报文段后，若同意建立连接，则发送确认报文，确认报文中SYN=1、ACK=1,确认号ack=x+1,同时随机选定一个自己序号seq=y,确认报文段同样不能携带数据，但是也要消耗掉一个序号，发送完毕后服务端进入<strong>SYN-RCVD</strong>（同步收到）状态</li>
<li><strong>第三次握手</strong>：客户端收到确认报文后，检查ACK=1，ack=x+1是否正确，若正确，则向服务端发送确认报文，确认报文中ACK=1,ack=y+1,seq=x+1,发送后进入<strong>ESTAB-LISHED</strong>状态，服务端收到确认报文后，也进入<strong>ESTAB-LISHED</strong>状态，此时双方TCP连接正式建立。</li>
</ul>
<p>上面的连接建立过程就是<strong>TCP三次握手</strong>。</p>
<h3><a name="t5"></a><a name="t5"></a><a id="_57"></a>六、为什么是三次握手？</h3>
<p>为什么client收到确认报文后，还要再发送一次确认报文给server呢？这主要是为了防止<strong>已失效的连接请求报文段</strong>突然又送到了Server端。<br>
假设现在TCP连接是<strong>两次握手机制</strong>，如下图server在收到client的连接请求，确认后就进入<strong>ESTAB-LISHED</strong>状态，会存在这样一种问题：<br>
client发送连接请求报文，由于网络原因，长时间阻塞在某个网络节点上了，于是client重传了一次请求连接报文，第二次请求报文正常达到server，连接正常建立，数据传输完毕后，释放连接。但是假设此时第一次发送的请求报文并没有丢失，而是延误一段时间才到达server,这本是已失效的连接请求报文段，但是server收到后，会以为是client重新发送的连接请求，于是向client发送确认报文后，进入<strong>ESTAB-LISHED</strong>状态，但是client并没有发出新建连接的请求，就会忽略server的确认报文，server却在一直等待client发送数据，导致server资源浪费严重。<br>
<img src="https://img-blog.csdnimg.cn/20191027094937488.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21hZ2ljXzEwMjQ=,size_16,color_FFFFFF,t_70" alt="两次握手"><br>
总结起来看，TCP三次握手过程就是client与server在相互确认各自发送和接收是否正常的过程：</p>
<ul>
<li><strong>第一次握手</strong>：client—&gt;server,server确认了cilent的发送能力和自己的接收能力是正常的</li>
<li><strong>第二次握手</strong>：server—&gt;client,client确认了自己的发送能力和server的接收能力是正常的，但是server此时不清楚自己的发送能力是否正常</li>
<li><strong>第三次握手</strong>：client—&gt;server,server确认了自己的发送能力正常，同时也表明双方也都确认完毕，可以开始传输数据。</li>
</ul>
<p>那么为什么不进行<strong>四次握手</strong>呢？这个问题留给你们解答。</p>

</div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-b6c3c6d139.css" rel="stylesheet">
</div>
